package com.qf.flash.widget;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.RectF;
import android.graphics.Shader;
import android.os.Build;
import android.support.annotation.NonNull;

import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.bumptech.glide.load.resource.bitmap.TransformationUtils;
import com.bumptech.glide.util.Preconditions;
import com.bumptech.glide.util.Synthetic;
import com.bumptech.glide.util.Util;

import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class RoundedCorner extends BitmapTransformation {
  private static final int HASH_MULTIPLIER = 31;
  private static final int HASH_ACCUMULATOR = 17;

  private static final String ID = "com.bumptech.glide.load.resource.bitmap.RoundedCorner";
  private static final byte[] ID_BYTES = ID.getBytes(CHARSET);

  private final int roundingRadius;
  private static final List<String> MODELS_REQUIRING_BITMAP_LOCK =
          Arrays.asList(
                  // Moto X gen 2
                  "XT1085",
                  "XT1092",
                  "XT1093",
                  "XT1094",
                  "XT1095",
                  "XT1096",
                  "XT1097",
                  "XT1098"
          );
  private static final Lock BITMAP_DRAWABLE_LOCK =
          MODELS_REQUIRING_BITMAP_LOCK.contains(Build.MODEL)
                  && Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1
                  ? new ReentrantLock() : new NoLock();

  /**
   * @param roundingRadius the corner radius (in device-specific pixels).
   * @throws IllegalArgumentException if rounding radius is 0 or less.
   */
  public RoundedCorner(int roundingRadius) {
    Preconditions.checkArgument(roundingRadius > 0, "roundingRadius must be greater than 0.");
    this.roundingRadius = roundingRadius;
  }

  /**
   * @param roundingRadius the corner radius (in device-specific pixels).
   * @throws IllegalArgumentException if rounding radius is 0 or less.
   *
   * @deprecated Use {@link #RoundedCorner(int)}
   */
  @Deprecated
  public RoundedCorner(@SuppressWarnings("unused") BitmapPool bitmapPool, int roundingRadius) {
    this(roundingRadius);
  }

  /**
   * @param roundingRadius the corner radius (in device-specific pixels).
   * @throws IllegalArgumentException if rounding radius is 0 or less.
   *
   * @deprecated Use {@link #RoundedCorner(int)}
   */
  @Deprecated
  public RoundedCorner(@SuppressWarnings("unused") Context context, int roundingRadius) {
    this(roundingRadius);
  }

  @Override
  protected Bitmap transform(
          @NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
    Matrix m = new Matrix();
    if (toTransform.getWidth() != outWidth || toTransform.getHeight() != outHeight) {
      // From ImageView/Bitmap.createScaledBitmap.
      final float scale;
      float dx = 0, dy = 0;

      if (toTransform.getWidth() * outHeight > outWidth * toTransform.getHeight()) {
        scale = (float) outHeight / (float) toTransform.getHeight();
        dx = (outWidth - toTransform.getWidth() * scale) * 0.5f;
      } else {
        scale = (float) outWidth / (float) toTransform.getWidth();
        dy = (outHeight - toTransform.getHeight() * scale) * 0.5f;
      }

      m.setScale(scale, scale);
      m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
    }


    Bitmap result = pool.get(outWidth, outHeight,getSafeConfig(toTransform));
    // We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given.
    TransformationUtils.setAlpha(toTransform, result);


    // Alpha is required for this transformation.
    Bitmap  bitmap= getAlphaSafeBitmap(pool, toTransform);
    result.setHasAlpha(true);


    Paint paint = new Paint(Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG);
    paint.setAntiAlias(true);

    Bitmap result2 = pool.get(outWidth, outHeight,getSafeConfig(result));
    RectF rect = new RectF(0, 0, result.getWidth(), result.getHeight());
    BITMAP_DRAWABLE_LOCK.lock();
    try {
      Canvas canvas = new Canvas(result);
      canvas.drawBitmap(bitmap, m, paint);
      canvas.setBitmap(null);


      Canvas canvas2 = new Canvas(result2);
      BitmapShader shader = new BitmapShader(result, Shader.TileMode.CLAMP,
              Shader.TileMode.CLAMP);
      paint.setShader(shader);
      canvas2.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
      canvas2.drawRoundRect(rect, roundingRadius, roundingRadius, paint);

    } finally {
      BITMAP_DRAWABLE_LOCK.unlock();
    }

    if (!bitmap.equals(toTransform)) {
      pool.put(bitmap);
    }
    return result2;
  }

  private static Bitmap getAlphaSafeBitmap(@NonNull BitmapPool pool,
                                           @NonNull Bitmap maybeAlphaSafe) {
    if (Bitmap.Config.ARGB_8888.equals(maybeAlphaSafe.getConfig())) {
      return maybeAlphaSafe;
    }

    Bitmap argbBitmap = pool.get(maybeAlphaSafe.getWidth(), maybeAlphaSafe.getHeight(),
            Bitmap.Config.ARGB_8888);
    new Canvas(argbBitmap).drawBitmap(maybeAlphaSafe, 0 /*left*/, 0 /*top*/, null /*paint*/);

    // We now own this Bitmap. It's our responsibility to replace it in the pool outside this method
    // when we're finished with it.
    return argbBitmap;
  }

  private static Bitmap.Config getSafeConfig(Bitmap bitmap) {
    return bitmap.getConfig() != null ? bitmap.getConfig() : Bitmap.Config.ARGB_8888;
  }


  @Override
  public boolean equals(Object o) {
    if (o instanceof RoundedCorner) {
      RoundedCorner other = (RoundedCorner) o;
      return roundingRadius == other.roundingRadius;
    }
    return false;
  }

  @Override
  public int hashCode() {
    return getHashCode(ID.hashCode(),
            getHashCode(roundingRadius, HASH_MULTIPLIER));
  }

  public  int getHashCode(int value, int accumulator) {
    return accumulator * HASH_MULTIPLIER + value;
  }

  @Override
  public void updateDiskCacheKey(MessageDigest messageDigest) {
    messageDigest.update(ID_BYTES);

    byte[] radiusData = ByteBuffer.allocate(4).putInt(roundingRadius).array();
    messageDigest.update(radiusData);
  }

  private static final class NoLock implements Lock {

    @Synthetic
    NoLock() { }

    @Override
    public void lock() {
      // do nothing
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
      // do nothing
    }

    @Override
    public boolean tryLock() {
      return true;
    }

    @Override
    public boolean tryLock(long time, @NonNull TimeUnit unit) throws InterruptedException {
      return true;
    }

    @Override
    public void unlock() {
      // do nothing
    }

    @NonNull
    @Override
    public Condition newCondition() {
      throw new UnsupportedOperationException("Should not be called");
    }
  }
}